﻿using UnityEngine;
using UnityEditor;
using UnityEngine.Networking;
using System;
using System.IO;
using System.Threading;
using xuni;

namespace xres
{


    public class DownloadFileAction : BaseAsyncAction
    {
        /// <summary>
        /// http服务器错误(一般无法恢复)
        /// </summary>
        public const string Err_Http = "Err_Http";
        /// <summary>
        /// 网络错误(比如: 弱网, 多试几次可能就成功了)
        /// </summary>
        public const string Err_Network = "Err_Network";
        /// <summary>
        /// 文件写出错误或下载字节不对
        /// </summary>
        public const string Err_Download_File = "Err_Download_File";
        //public const string Err_Write_External = "Err_Write_External";
        public const string Err_Crc32 = "Err_Crc32";


        FileDownloadHandler _downloadHandler;

        bool _addTimeStamp;
        internal string _fileUrl = "";
        long _verifyBytes = 0;
        uint _crc32 = 0;

        int _leftRetryCount = 0;
        AsyncOperation _op;

        FileInfo _fi;
        public string FilePath { get { return _fi.FullName; } }

        public long CheckDownBytes()
        {
            switch (_state)
            {
            case BaseAsyncActionState.Init:
                return 0;

            case "Download":
                if (_downloadHandler.TotalBytes > 0)
                    return _downloadHandler.HaveDownBytes;
                return 0;
            }

            if (null == _downloadHandler) //没下载就直接结束的情况
                return _verifyBytes;

            return _downloadHandler.HaveDownBytes;
        }

        public float CheckProgress()
        {
            switch (_state)
            {
            case BaseAsyncActionState.Init:
                return 0;

            case "Download":
                if (false) Debug.Log($"DownloadFile: prg: {_op.progress}, url: {_fileUrl}");
                return _op.progress;
            }

            return 1;
        }

        /// <summary>
        /// </summary>
        /// <param name="url">文件下载地址</param>
        /// <param name="savePath">文件保存路径(可以是文件夹)</param>
        /// <param name="isFile">savePath是文件路径还是文件夹路径</param>
        /// <param name="verifyBytes">用这个大小来校验实际下载文件大小</param>
        /// <param name="crc32">要使用断点续传, 需要有crc校验码(否则无法校验断点续传下载的正确性, 比如: 第1次时v2文件, 下次断点续传的是v11的文件)</param>
        public void Download(string url, bool addTimeStamp, string savePath, bool isFile, long verifyBytes, uint crc32)
        {
            switch (_state)
            {
            case BaseAsyncActionState.Init:
            case BaseAsyncActionState.Finish:
                //可用于重复执行任务
                break;

            default:
                return;
            }

            _leftRetryCount = 3;
            _addTimeStamp = addTimeStamp;
            _fileUrl = url;
            _verifyBytes = verifyBytes;
            _crc32 = crc32;

            var filePath = savePath;
            if (!isFile) //path是dir路径(使用url中的文件名作为文件名)
            {
                var fileName = HttpUtils.GetLastSegment(url);

                var fileDir = PathUtils.DirEndWithSeparator(savePath);
                filePath = xuni.StringUtils.Builder.Append(fileDir).Append(fileName).BuildString();
            }
            if (false) Debug.Log($"DownloadFile: {filePath}");

            _fi = new FileInfo(filePath);
            if (verifyBytes > 0 && _fi.Exists)
            {
                var diff = _fi.Length - verifyBytes;
                if (0 == diff)
                {
                    Debug.Log($"DownloadFile: exist file is ok, no need down: {_fileUrl}");

                    _state = "CheckCrc32";
                    ThreadPool.QueueUserWorkItem(CheckCrc32OnThread);
                    return;
                }

                if (diff > 0) //超过字节了, 肯定是之前下载的某个老版本的文件
                {
                    File.Delete(filePath);
                }
            }

            _downloadHandler = new FileDownloadHandler(filePath);

            _state = "Download";
            SendRequest();
        }

        void SendRequest()
        {
            if (_leftRetryCount < 3)
            {
                _fi.Refresh();
            }

            try
            {
                var rangeValue = "";
                if (_crc32 > 0 && (DateTime.UtcNow - _fi.LastWriteTimeUtc).TotalHours <= 1) //1小时以内才断点续传了
                {
                    var range = _downloadHandler.PrepareDownload(true);

                    var sb = xuni.StringUtils.Builder;
                    rangeValue = sb.Append("bytes=").Append(range).Append("-").BuildString();
                    if (true) Debug.Log($"DownloadFile: breakpoint down: range: {rangeValue}, {_fileUrl}");
                }
                else
                {
                    _downloadHandler.PrepareDownload(false);
                    if (false) Debug.Log($"DownloadFile: normal down: {_fileUrl}");
                }

                --_leftRetryCount;

                var url = "";
                if (_addTimeStamp)
                    url = HttpUtils.AddTimeStamp(_fileUrl);
                else
                    url = _fileUrl;

                var webReq = UnityWebRequest.Get(url);
                if (!string.IsNullOrEmpty(rangeValue))
                    webReq.SetRequestHeader("Range", rangeValue);
                //webReq.timeout = 10;

                webReq.downloadHandler = _downloadHandler;
                var request = webReq.SendWebRequest();
                _op = request;
                request.completed += OnComplete_HttpGetFile;
            }
            catch (Exception ex)
            {
                SetError(Err_Download_File, $"DownloadFile: PrepareDownload fail: {_fileUrl}");
                Debug.Log(ErrMsg);
                SetFinishAndNotify(ErrMsg);
            }
        }

        void OnComplete_HttpGetFile(AsyncOperation op)
        {
            _op = null;

            var request = (UnityWebRequestAsyncOperation)op;
            var webReq = request.webRequest;
            using (webReq)
            {
                _downloadHandler.EndDownload();

                var setNull = true;
                //Debug.Log($"DownFile: isDone:{webReq.isDone}, netErr:{webReq.isNetworkError}");
                if (!webReq.isDone || webReq.isNetworkError) //超时或者网络不通
                {
                    if (_leftRetryCount <= 0)
                    {
                        SetError(Err_Network, $"DownloadFile: leftRetry <= 0, end, {_fileUrl}");
                        Debug.Log(ErrMsg);
                        SetFinishAndNotify(ErrMsg);
                    }
                    else
                    {
                        Debug.Log($"DownloadFile: timeour or neterr, leftRetry: {_leftRetryCount}, {_fileUrl}");
                        SendRequest();
                        setNull = false;
                    }
                }
                else if (webReq.isHttpError || !string.IsNullOrEmpty(webReq.error)) //http错误, 重试没用
                {
                    if (400 == webReq.responseCode) //断点下载的range字节太大(比如: 使用了在老版本文件上进行了断点下载, 老版本文件已下载部分已经超过了新版本的最大长度)
                    {
                        Debug.Log($"DownloadFile: old file breakpoint");
                        File.Delete(_fi.FullName);
                    }

                    SetError(Err_Http, $"DownloadFile: http error: {webReq.error}, {_fileUrl}");
                    Debug.Log(ErrMsg);
                    SetFinishAndNotify(ErrMsg);
                }
                else
                {
                    if (!string.IsNullOrEmpty(_downloadHandler.ErrType))
                    {
                        SetError(Err_Download_File, _downloadHandler.ErrType);
                        SetFinishAndNotify(ErrMsg);
                    }
                    else
                    {
                        _fi.Refresh();
                        if (_verifyBytes > 0 && _verifyBytes != _fi.Length)
                        {
                            Debug.Log($"DownloadFile: size fail: my:{_fi.Length}, server:{_verifyBytes}");
                            //File.Delete(_fi.FullName); //下载的文件不对, 就直接删除
                            SetError(Err_Download_File, _downloadHandler.ErrType);
                            SetFinishAndNotify(ErrMsg);
                        }
                        else
                        {
                            if (_crc32 > 0)
                            {
                                _state = "CheckCrc32";
                                ThreadPool.QueueUserWorkItem(CheckCrc32OnThread);
                            }
                            else
                            {
                                SetError("", "");
                                SetFinishAndNotify("");
                            }
                        }
                    }
                }

                //CheckDownBytes要用
                /*
                if (setNull)
                {
                    _downloadHandler = null;
                }
                */
            }
        }

        void CheckCrc32OnThread(object state)
        {
            var errMsg = "";
            var crc = 0;
            try
            {
                var crc32 = Crc32Utils.FileCrc32(_fi.FullName, null, null);
                crc = (int)crc32;
            }
            catch (Exception ex)
            {
                errMsg = ex.Message;
            }

            MainThreadSync.QueueOnMainThread(OnComplete_Crc32, crc, errMsg);
        }

        void OnComplete_Crc32(UserData userData)
        {
            var errMsg = userData.s;
            var crc32 = (uint)userData.i;

            if (!string.IsNullOrEmpty(errMsg) || (crc32 != _crc32))
            {
                Debug.Log($"DownloadFile: crc32 fail: {errMsg}, my:{crc32}, server:{_crc32}");
                //File.Delete(_fi.FullName); //下载的文件不对, 就直接删除

                SetError(Err_Crc32, errMsg);
                SetFinishAndNotify(ErrMsg);
            }
            else
            {
                SetError("", "");
                SetFinishAndNotify("");
            }
        }


    } //end of class


}